#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("HI");
#define DeviceName "my_cdev"
#define CMD_IOC_MAGIC 'k' //自定义类型，应用层与驱动应一样
#define CMD_RESET _IO(CMD_IOC_MAGIC, 0)
//#define CMD_WRITE		_IOR(CMD_IOC_MAGIC, 1, int)//int表示交互数据类型为int
//#define CMD_READ		_IOW(CMD_IOC_MAGIC, 2, int)
#define CMD_WRITE _IOR(CMD_IOC_MAGIC, 1, int) // char*表示交互数据类型为char*
#define CMD_READ _IOW(CMD_IOC_MAGIC, 2, int)
#define DATALEN 4096
static dev_t devno;
static char data[DATALEN];
static int datalen = 0;
static int demo_open(struct inode *ind, struct file *fp)
{
	printk("my test dev open\n");
	return 0;
}

static int demo_release(struct inode *ind, struct file *fp)
{
	printk("my test dev release\n");
	return 0;
}

static long demo_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
	switch (cmd)
	{
	case CMD_RESET:
		printk("cmd 0: no argument.\n");
		break;
	case CMD_WRITE:
		if (copy_from_user(data, (const char __user *)arg, DATALEN))
		{
			printk("my cdev write failed\n");
			return -EFAULT;
		}
		datalen += strlen(data);
		break;
	case CMD_READ:
		if (0 == datalen)
		{
			copy_to_user((char __user *)arg, "no data", 8);
			break;
		}
		else if (copy_to_user((char __user *)arg, data, datalen))
		{
			printk("my cdev read failed\n");
			return -EFAULT;
		}
		datalen -= datalen;
		break;
	default:
		pr_err("%s: invalid command.\n", __func__);
		return -ENOTTY;
	}
	return 0;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = demo_open,
	.release = demo_release,
	.unlocked_ioctl = demo_ioctl,
};

static struct cdev cd;

static int demo_init(void)
{
	int rc;
	rc = alloc_chrdev_region(&devno, 0, 1, DeviceName);
	if (rc < 0)
	{
		pr_err("alloc_chrdev_region failed!");
		return rc;
	}
	printk("MAJOR is %d\n", MAJOR(devno));
	printk("MINOR is %d\n", MINOR(devno));

	cdev_init(&cd, &fops);
	rc = cdev_add(&cd, devno, 1);
	if (rc < 0)
	{
		pr_err("cdev_add failed!");
		return rc;
	}
	printk("my test dev init success\n");
	return 0;
}

static void demo_exit(void)
{
	cdev_del(&cd);
	unregister_chrdev_region(devno, 1);
	printk("my test dev exit success\n");
	return;
}

module_init(demo_init);
module_exit(demo_exit);
